package com.tc.data.spring;

import com.alibaba.fastjson.JSON;
import com.tc.data.common.model.MatrixDatasourceModel;
import com.tc.data.driver.DataSeedExceptionInterceptor;
import com.tc.data.spring.util.Constants;
import com.tc.property.PropertyHolder;
import com.tc.property.support.PropertiesUtils;
import com.tc.data.common.annotation.Strategy;
import com.tc.data.common.model.AtomModel;
import com.tc.data.common.model.GroupModel;
import com.tc.data.common.model.MatrixDatasourceModel;
import com.tc.data.datasource.ReadWriteDataSource;
import com.tc.data.datasource.ReadWriteDataSourceKey;
import com.tc.data.datasource.RepositoryShardingDataSource;
import com.tc.data.datasource.interceptor.AnnotationReadWriteDataSourceInterceptor;
import com.tc.data.datasource.interceptor.RepositoryShardingDataSourceInterceptor;
import com.tc.data.datasource.interceptor.TableShardingDataSourceInterceptor;
import com.tc.data.driver.DataSeedExceptionInterceptor;
import com.tc.data.mybatis.converter.DefaultSqlConverter;
import com.tc.data.mybatis.plugin.ShardingPlugin;
import com.tc.data.preheat.DatabasePreheatListener;
import com.tc.data.spring.util.Utils;
import com.tc.data.spring.validator.TransactionAnnotationPropagationValidator;
import com.tc.data.spring.validator.TransactionAnnotationValidateAdvisor;
import com.tc.data.spring.validator.TransactionAnnotationValidator;
import com.tc.data.spring.validator.TransactionAnnotationValueValidator;
import com.tc.data.spring.vo.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.plugin.Interceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.ManagedArray;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.config.TransactionManagementConfigUtils;
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.lang.annotation.Annotation;
import java.util.*;

public class MatrixDatasourceBeanDefinitionParser implements BeanDefinitionParser, Constants {

	private static final Logger LOGGER = LoggerFactory.getLogger(MatrixDatasourceBeanDefinitionParser.class);

	private String dataSourceId;

	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		PropertiesUtils.getPropsConfig(element);

		String matrixName = element.getAttribute(XSD_MATRIX_NAME);
		if (StringUtils.isBlank(matrixName)) {
			String errMsg = "matrixName can not be null. Please check matrix-name.";
			LOGGER.error(errMsg);
			parserContext.getReaderContext().error(errMsg, parserContext.extractSource(element));
		}

		MatrixDatasourceModel matrixDatasourceModel = null;
		String matrixData = StringUtils.EMPTY;
		try {
			matrixData = PropertyHolder.getProperties().getProperty(buidMatrixDataKey(matrixName));
			if (StringUtils.isNotEmpty(matrixData)) {
				matrixDatasourceModel = JSON.parseObject(matrixData, MatrixDatasourceModel.class);
			}
		} catch (Exception e) {
			LOGGER.error("parse matrixDatasourceModel error.", e);
		}

		if (null == matrixDatasourceModel) {
			String errMsg = "matrixDatasourceModel can not be null. Please check matrix data config.";
			LOGGER.error(errMsg);
			parserContext.getReaderContext().error(errMsg, parserContext.extractSource(element));
		}
		DataSourceMetaVO dataSourceMetaVO = parseDataSource(element, matrixDatasourceModel);
		dataSourceId = dataSourceMetaVO.getId();
		if (StringUtils.isNotBlank(matrixData)) {
			//MetricRegister.registerMatrix(matrixDatasourceModel.getMatrixName(),com.vip.venus.data.metric.util.Utils.trimCfg(matrixData));
		}
		registerDsBeanDefinition(element, parserContext, dataSourceMetaVO, matrixDatasourceModel.getMatrixName());
		registerWarmListener(parserContext, matrixName);
		return null;
	}

	private void registerWarmListener(ParserContext parserContext, String matrixName) {
		RootBeanDefinition databaseWarmListener = new RootBeanDefinition(DatabasePreheatListener.class);
		databaseWarmListener.getPropertyValues().add(MATRIX_NAME, matrixName);
		parserContext.getRegistry().registerBeanDefinition(dataSourceId + "DatabaseWarmListener", databaseWarmListener);
	}

	private void registerDsBeanDefinition(Element element, ParserContext parserContext,
			DataSourceMetaVO dataSourceMetaVO, String matrixName) {
		List<ReadWriteDataSourceMetaVO> readWriteDataSourceMetaVOs = dataSourceMetaVO.getReadWriteDataSourceMetaVOs();
		if (CollectionUtils.isEmpty(readWriteDataSourceMetaVOs)) {
			return;
		}
		// 增加annotation的creator
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);

		// dsmap放置所有的写数据源。如果只有一个写库，则为一个。 如果有一个读写分离，也只有写库。如果有多个写库，则为多个.
		// 其中value为连接池
		ManagedMap<String, RuntimeBeanReference> dsMap = new ManagedMap<String, RuntimeBeanReference>();
		ManagedMap<String, RuntimeBeanReference> dsKeyMap = new ManagedMap<String, RuntimeBeanReference>();
		Map<String, String> atom2GroupMap = new LinkedHashMap<>();
		ManagedMap<String, RuntimeBeanReference> atoms4MatrixMap = new ManagedMap<String, RuntimeBeanReference>();

		String datasourceBeanName = null;
		RootBeanDefinition writeDataSource = null;
		for (ReadWriteDataSourceMetaVO readWriteDataSourceMetaVO : readWriteDataSourceMetaVOs) {
			// 放置的所有读库和写库，只有同时存在读写分写则生效
			ManagedMap<String, RuntimeBeanReference> rwdsMap = new ManagedMap<String, RuntimeBeanReference>();
			// 放置所有读库和写库，只有同时存在读写分写则生效
			ManagedMap<String, RuntimeBeanReference> atoms4GroupMap = new ManagedMap<String, RuntimeBeanReference>();
			if (writeDataSource == null || !dataSourceMetaVO.isCaelusDatasource()) {
				// 写原子数据源
				AtomDataSourceMetaVO writeDataSourceMetaVO = readWriteDataSourceMetaVO.getWriteDataSourceMetaVO();
				writeDataSource = new RootBeanDefinition(dataSourceMetaVO.getDataSourceClass());
				writeDataSource.getPropertyValues().addPropertyValues(dataSourceMetaVO.getProperties());
				writeDataSource.getPropertyValues().addPropertyValues(writeDataSourceMetaVO.getProperties());
				if (dataSourceMetaVO.isCaelusDatasource()) {
					writeDataSource.getPropertyValues().addPropertyValue("datasourceMeta", dataSourceMetaVO);
				} else {
					writeDataSource.getPropertyValues().addPropertyValues(Utils.getDbLoginPropertiesFromAtom(
							writeDataSourceMetaVO.getAtomModel(), dataSourceMetaVO.getDsType()));
				}
				// only for caelus datasource
				datasourceBeanName = buildDatasourceBeanName(dataSourceId,
						readWriteDataSourceMetaVO.getWriteBeanDefinitionName(), dataSourceMetaVO.isCaelusDatasource());
			}
			// 读原子数据源
			List<AtomDataSourceMetaVO> readDataSourceMetaVOs = readWriteDataSourceMetaVO.getReadDataSourceMetaVOs();
			if (CollectionUtils.isNotEmpty(readDataSourceMetaVOs)) {
				int i = 0;
				for (AtomDataSourceMetaVO readDataSourceMetaVO : readDataSourceMetaVOs) {
					String readBeanDefinitionNameKey = readWriteDataSourceMetaVO.getReadBeanDefinitionName(i++);
					String readDataSourceName = buildDatasourceBeanName(dataSourceId, readBeanDefinitionNameKey,
							dataSourceMetaVO.isCaelusDatasource());

					if (!dataSourceMetaVO.isCaelusDatasource()) {
						RootBeanDefinition readDataSource = new RootBeanDefinition(
								dataSourceMetaVO.getDataSourceClass());
						readDataSource.getPropertyValues().addPropertyValues(dataSourceMetaVO.getProperties());
						readDataSource.getPropertyValues().addPropertyValues(readDataSourceMetaVO.getProperties());
						readDataSource.getPropertyValues().addPropertyValues(Utils.getDbLoginPropertiesFromAtom(
								readDataSourceMetaVO.getAtomModel(), dataSourceMetaVO.getDsType()));
						parserContext.getRegistry().registerBeanDefinition(readDataSourceName, readDataSource);
					}
					rwdsMap.put(readBeanDefinitionNameKey, new RuntimeBeanReference(readDataSourceName));
					if (StringUtils.isNotBlank(readDataSourceMetaVO.getLogicName())) {
						atoms4GroupMap.put(readDataSourceMetaVO.getLogicName(),
								new RuntimeBeanReference(readDataSourceName));
					}
				}

				// 负载均衡
				RootBeanDefinition loadBalance = new RootBeanDefinition(
						readWriteDataSourceMetaVO.getLoadBalanceClass());
				loadBalance.getConstructorArgumentValues()
						.addGenericArgumentValue(readWriteDataSourceMetaVO.getLoadBalanceArgs());
				parserContext.getRegistry().registerBeanDefinition(
						buildBeanName(dataSourceId, readWriteDataSourceMetaVO.getLoadBalanceBeanDefinitionName()),
						loadBalance);

				// 读写Key
				RootBeanDefinition readWriteDataSourceKey = new RootBeanDefinition(ReadWriteDataSourceKey.class);
				readWriteDataSourceKey.getPropertyValues().add(READ_DATESOURCES,readWriteDataSourceMetaVO.getReadKeys());
				readWriteDataSourceKey.getPropertyValues().add(WRITE_KEY, readWriteDataSourceMetaVO.getWriteKey());
				readWriteDataSourceKey.getPropertyValues().add(STRATEGY, new RuntimeBeanReference(
						buildBeanName(dataSourceId, readWriteDataSourceMetaVO.getLoadBalanceBeanDefinitionName())));
				parserContext.getRegistry().registerBeanDefinition(
						buildBeanName(dataSourceId, readWriteDataSourceMetaVO.getReadWriteKeyBeanDefinitionName()),
						readWriteDataSourceKey);

				// 读写数据源
				RootBeanDefinition readWriteDataSource = new RootBeanDefinition(ReadWriteDataSource.class);
				readWriteDataSource.getPropertyValues().add(DATASOURCES, atoms4GroupMap);
				readWriteDataSource.getPropertyValues().add(GROUP_NAME, readWriteDataSourceMetaVO.getName());
				readWriteDataSource.getPropertyValues().add(MATRIX_NAME, matrixName);
				readWriteDataSource.getPropertyValues().add(TARGET_DATASOURCES, rwdsMap);
				readWriteDataSource.getPropertyValues().add(DEFAULT_TARGET_DATASOURCE,
						new RuntimeBeanReference(datasourceBeanName));
				readWriteDataSource.getPropertyValues().add(DATASOURCE_KEY, new RuntimeBeanReference(
						buildBeanName(dataSourceId, readWriteDataSourceMetaVO.getReadWriteKeyBeanDefinitionName())));
				parserContext.getRegistry().registerBeanDefinition(
						buildBeanName(dataSourceId, readWriteDataSourceMetaVO.getName()), readWriteDataSource);

				dsMap.put(readWriteDataSourceMetaVO.getName(),
						new RuntimeBeanReference(buildBeanName(dataSourceId, readWriteDataSourceMetaVO.getName())));
				dsKeyMap.put(readWriteDataSourceMetaVO.getName(), new RuntimeBeanReference(
						buildBeanName(dataSourceId, readWriteDataSourceMetaVO.getReadWriteKeyBeanDefinitionName())));

				// 注册写原子数据源
				parserContext.getRegistry().registerBeanDefinition(datasourceBeanName, writeDataSource);
				rwdsMap.put(readWriteDataSourceMetaVO.getWriteBeanDefinitionName(),
						new RuntimeBeanReference(datasourceBeanName));

				if (StringUtils.isNotBlank(readWriteDataSourceMetaVO.getWriteDataSourceMetaVO().getLogicName())) {
					atoms4GroupMap.put(readWriteDataSourceMetaVO.getWriteDataSourceMetaVO().getLogicName(),
							new RuntimeBeanReference(datasourceBeanName));
				}
			} else {

				String dsBeanName = dataSourceMetaVO.isCaelusDatasource() ? datasourceBeanName
						: buildBeanName(dataSourceId, readWriteDataSourceMetaVO.getName());

				dsMap.put(readWriteDataSourceMetaVO.getName(), new RuntimeBeanReference(dsBeanName));

				// FIXME caelus时，代码会重复执行
				// 注册写原子数据源，伪装成读写数据源
				parserContext.getRegistry().registerBeanDefinition(dsBeanName, writeDataSource);

				if (StringUtils.isNotBlank(readWriteDataSourceMetaVO.getWriteDataSourceMetaVO().getLogicName())) {
					atoms4MatrixMap.put(readWriteDataSourceMetaVO.getWriteDataSourceMetaVO().getLogicName(),
							new RuntimeBeanReference(dsBeanName));
					atom2GroupMap.put(readWriteDataSourceMetaVO.getWriteDataSourceMetaVO().getLogicName(),
							dsBeanName);
				}
			}
		}

		// 数据源
		RootBeanDefinition dataSource = new RootBeanDefinition(RepositoryShardingDataSource.class);
		dataSource.getPropertyValues().add(DATASOURCES, atoms4MatrixMap);
		dataSource.getPropertyValues().add(ATOM_TO_GROUP_MAP, atom2GroupMap);
		dataSource.getPropertyValues().add(MATRIX_NAME, matrixName);
		dataSource.getPropertyValues().add(TARGET_DATASOURCES, dsMap);
		// 当只有一个库时即不存在分库的场景时使用该库作为默认的分库，而当存在多分库时不设置默认分库以防止数据乱写
		if (dsMap.size() == 1) {
			RuntimeBeanReference defaultReadWrite = dsMap.values().iterator().next();
			dataSource.getPropertyValues().add(DEFAULT_TARGET_DATASOURCE, defaultReadWrite);
		}
		parserContext.getRegistry().registerBeanDefinition(dataSourceMetaVO.getId(), dataSource);

		// 分库/分表策略扫描器
		StrategiesClassPathBeanDefinitionScanner scan = new StrategiesClassPathBeanDefinitionScanner(
				parserContext.getRegistry());
		scan.setAnnotationClass(Strategy.class); //Strategy 注解标识的类注册到容器
		scan.registerFilters();

		Document appDoc = element.getOwnerDocument();
		BeanDefinitionParserDelegate beanDefDelegate = parserContext.getDelegate();

		RepositoryShardingMetaVO repositoryShardingMetaVO = dataSourceMetaVO.getRepositoryShardingMetaVO();
		if (repositoryShardingMetaVO != null) {
			registryRepositorySharding(element, parserContext, dataSourceMetaVO, dsKeyMap, scan, appDoc,
					beanDefDelegate, repositoryShardingMetaVO, dataSourceMetaVO.getReadWriteMetaVO());
		}

		registryTableSharding(parserContext, dataSourceMetaVO, scan, appDoc, beanDefDelegate);

		// TODO 没有配置分库分表策略的情况下，解决读写分离不生效问题
		registryReadWriteInterceptor(parserContext, dsKeyMap, appDoc, beanDefDelegate,
				dataSourceMetaVO.getReadWriteMetaVO());

		registryTransaction(parserContext, dataSourceMetaVO.getTransactionManager());
	}

	private void registryTransaction(ParserContext parserContext, String transactionManger) {
		if (StringUtils.isNotBlank(transactionManger)) {
			String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;

			if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {

				// Create the TransactionAttributeSource definition.
				RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);
				sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				parserContext.getRegistry().registerBeanDefinition("validAnnotationTransactionAttributeSource",
						sourceDef);
						// sourceName =
						// parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

				// Create the TransactionInterceptor definition.
				RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
				interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				interceptorDef.getPropertyValues().add("transactionManagerBeanName", transactionManger);
				interceptorDef.getPropertyValues().add("transactionAttributeSource",
						new RuntimeBeanReference("validAnnotationTransactionAttributeSource"));
				String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

				// Create the TransactionAttributeSourceAdvisor definition.
				RootBeanDefinition advisorDef = new RootBeanDefinition(
						BeanFactoryTransactionAttributeSourceAdvisor.class);
				advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
				advisorDef.getPropertyValues().add("transactionAttributeSource",
						new RuntimeBeanReference("validAnnotationTransactionAttributeSource"));
				advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
				advisorDef.getPropertyValues().add("order", TRANSACTION_ADVISOR_ORDER);
				parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);

				RootBeanDefinition validateAdvisor = new RootBeanDefinition(TransactionAnnotationValidateAdvisor.class);
				List<TransactionAnnotationValidator> annotationValidators = new ArrayList<>();

				TransactionAnnotationPropagationValidator validator;
				String whitelist = PropertyHolder.getProperties().getProperty("transactional.propagation.whitelist");
				if (StringUtils.isBlank(whitelist))
					validator = new TransactionAnnotationPropagationValidator();
				else
					validator = new TransactionAnnotationPropagationValidator(new HashSet<>(Arrays.asList(whitelist.split(","))));

				annotationValidators.add(validator);
				validateAdvisor.getPropertyValues().add("validator", annotationValidators);
				parserContext.getRegistry().registerBeanDefinition("transactionAnnotationValidator", validateAdvisor);
			} else {
				BeanDefinition validateAdvisor = parserContext.getRegistry().getBeanDefinition("transactionAnnotationValidator");
				@SuppressWarnings("unchecked")
				List<TransactionAnnotationValidator> annotationValidators = ((List<TransactionAnnotationValidator>) validateAdvisor.getPropertyValues().get("validator"));
				addTransactionAnnotationValueValidator(annotationValidators);
			}
		}

	}

	private void addTransactionAnnotationValueValidator(List<TransactionAnnotationValidator> validators) {
		for (TransactionAnnotationValidator validator : validators) {
			if (validator instanceof TransactionAnnotationValueValidator)
				return;
		}

		validators.add(new TransactionAnnotationValueValidator());
	}

	private void registryRepositorySharding(Element element, ParserContext parserContext,
                                            DataSourceMetaVO dataSourceMetaVO, ManagedMap<String, RuntimeBeanReference> dsKeyMap,
                                            StrategiesClassPathBeanDefinitionScanner scan, Document appDoc,
                                            BeanDefinitionParserDelegate beanDefDelegate, RepositoryShardingMetaVO repositoryShardingMetaVO,
                                            ReadWriteMetaVO readWriteMetaVO) {

		// 分库拦截器
		if (StringUtils.isNotBlank(repositoryShardingMetaVO.getStrategiesPackage())) {
			RootBeanDefinition repositoryShardingDataSourceInterceptor = new RootBeanDefinition(
					RepositoryShardingDataSourceInterceptor.class);
			ManagedMap<String, BeanDefinitionHolder> repositoryStrategiesManagedMap = scanRepositoryStrategies(scan,
					repositoryShardingMetaVO.getStrategiesPackage());
			repositoryShardingDataSourceInterceptor.getPropertyValues().add(REPOSITORY_SHARDING_STRATEGIES,
					repositoryStrategiesManagedMap);
			parserContext.getRegistry().registerBeanDefinition(
					buildBeanName(dataSourceId, REPOSITORY_SHARDING_DATASOURCE_INTERCEPTOR),
					repositoryShardingDataSourceInterceptor);

			// 分库aop:config
			beanDefDelegate.parseCustomElement(buildRepositoryShardingAopConfigElmt(appDoc, repositoryShardingMetaVO));
		}

	}

	/**
	 *分表拦截器依赖 mybatis
	 */
	private void registryTableSharding(ParserContext parserContext, DataSourceMetaVO dataSourceMetaVO,
                                       StrategiesClassPathBeanDefinitionScanner scan, Document appDoc,
                                       BeanDefinitionParserDelegate beanDefDelegate) {
		TableShardingMetaVO tableShardingMetaVO = dataSourceMetaVO.getTableShardingMetaVO();
		if (tableShardingMetaVO != null) {
			// 分表拦截器
			RootBeanDefinition tableShardingDataSourceInterceptor = new RootBeanDefinition(
					TableShardingDataSourceInterceptor.class);
			ManagedMap<String, BeanDefinitionHolder> tableStrategies = scanTableStrategies(scan,
					tableShardingMetaVO.getStrategiesPackage());
			tableShardingDataSourceInterceptor.getPropertyValues().add(TABLE_SHARDING_STRATEGIES, tableStrategies);
			parserContext.getRegistry().registerBeanDefinition(
					buildBeanName(dataSourceId, TABLE_SHARDING_DATASOURCE_INTERCEPTOR),
					tableShardingDataSourceInterceptor);

			// 分表aop:config
			beanDefDelegate.parseCustomElement(buildTableShardingAopConfigElmt(appDoc, tableShardingMetaVO));

			// 分表插件
			RootBeanDefinition shardingPlugin = new RootBeanDefinition(ShardingPlugin.class);
			shardingPlugin.getPropertyValues().add(SQL_CONVERTER,
					new BeanDefinitionHolder(new RootBeanDefinition(DefaultSqlConverter.class), buildBeanName(dataSourceId, SQL_CONVERTER)));
			parserContext.getRegistry().registerBeanDefinition(buildBeanName(dataSourceId, SHARDING_PLUGIN),shardingPlugin);

			// 注入分表插件
			if (parserContext.getRegistry().containsBeanDefinition(dataSourceMetaVO.getMyBatisSqlSessionFactory())) {
				BeanDefinition sqlSessionFactory = parserContext.getRegistry().getBeanDefinition(dataSourceMetaVO.getMyBatisSqlSessionFactory());
				ManagedArray plugins = (ManagedArray) sqlSessionFactory.getPropertyValues().get(PLUGINS);
				if (plugins == null) {
					plugins = new ManagedArray(Interceptor.class.getName(), 1);
					sqlSessionFactory.getPropertyValues().add(PLUGINS, plugins);
				}
				plugins.add(0, new RuntimeBeanReference(buildBeanName(dataSourceId, SHARDING_PLUGIN)));
			}
		}
	}

	/**
	 * registry read write interceptor
	 * 
	 * @param parserContext
	 * @param dsKeyMap
	 * @param appDoc
	 * @param beanDefDelegate
	 * @param readWriteMetaVO
	 */
	private void registryReadWriteInterceptor(ParserContext parserContext,
                                              ManagedMap<String, RuntimeBeanReference> dsKeyMap, Document appDoc,
                                              BeanDefinitionParserDelegate beanDefDelegate, ReadWriteMetaVO readWriteMetaVO) {
		RootBeanDefinition annotationReadWriteDataSourceInterceptor = new RootBeanDefinition(AnnotationReadWriteDataSourceInterceptor.class);
		annotationReadWriteDataSourceInterceptor.getPropertyValues().add(READ_WRITE_DATASOURCE_KEYS, dsKeyMap);
		parserContext.getRegistry().registerBeanDefinition(
				buildBeanName(dataSourceId, ANNOTATION_READ_WRITE_DATASOURCE_INTERCEPTOR),
				annotationReadWriteDataSourceInterceptor);
		beanDefDelegate.parseCustomElement(buildAnnotationReadWriteAopConfigElmt(appDoc, readWriteMetaVO));
	}

	private static String buildDatasourceBeanName(String dataSourceId, String beanName, boolean isCealus) {
		return isCealus ? dataSourceId + "_caelus" : dataSourceId + "_" + beanName;
	}

	private static String buildBeanName(String dataSourceId, String beanName) {
		return dataSourceId + "_" + beanName;
	}

	private static String buidMatrixDataKey(String matrixName) {
		return RESOURCE_RDBMS_MATRIX_PREFIX + "/" + matrixName;
	}

	/**
	 * build TableSharding aop config element
	 * 
	 * @param appDoc
	 * @param tableShardingMetaVO
	 * @return
	 */
	private Element buildTableShardingAopConfigElmt(Document appDoc, TableShardingMetaVO tableShardingMetaVO) {
		Element tableShardingAopConfigElmt = appDoc.createElementNS(AOP_NAMESPACE_URI, CONFIG);

		Element tableShardingPointcutChild = appDoc.createElementNS(AOP_NAMESPACE_URI, POINTCUT);
		tableShardingPointcutChild.setAttribute(XSD_ID,
				buildBeanName(dataSourceId, TABLE_SHARDING_DATA_SOURCE_POINTCUT));
		tableShardingPointcutChild.setAttribute(EXPRESSION, tableShardingMetaVO.getPointcutExpression());
		tableShardingAopConfigElmt.appendChild(tableShardingPointcutChild);

		Element tableShardingAdvisorChild = appDoc.createElementNS(AOP_NAMESPACE_URI, ADVISOR);
		tableShardingAdvisorChild.setAttribute(POINTCUT_REF,
				buildBeanName(dataSourceId, TABLE_SHARDING_DATA_SOURCE_POINTCUT));
		tableShardingAdvisorChild.setAttribute(ADVICE_REF,
				buildBeanName(dataSourceId, TABLE_SHARDING_DATASOURCE_INTERCEPTOR));
		tableShardingAdvisorChild.setAttribute(XSD_MATRIX_ORDER, TABLE_SHARDING_DATA_SOURCE_POINTCUT_ORDER);
		tableShardingAopConfigElmt.appendChild(tableShardingAdvisorChild);
		return tableShardingAopConfigElmt;
	}

	/**
	 * build RepositorySharding aop config element
	 * 
	 * @param appDoc
	 * @param repositoryShardingMetaVO
	 * @return
	 */
	private Element buildRepositoryShardingAopConfigElmt(Document appDoc,
			RepositoryShardingMetaVO repositoryShardingMetaVO) {
		Element repositoryShardingAopConfigElmt = appDoc.createElementNS(AOP_NAMESPACE_URI, CONFIG);

		Element repositoryShardingPointcutChild = appDoc.createElementNS(AOP_NAMESPACE_URI, POINTCUT);
		repositoryShardingPointcutChild.setAttribute(XSD_ID,
				buildBeanName(dataSourceId, REPOSITORY_SHARDING_DATA_SOURCE_POINTCUT));
		repositoryShardingPointcutChild.setAttribute(EXPRESSION, repositoryShardingMetaVO.getPointcutExpression());
		repositoryShardingAopConfigElmt.appendChild(repositoryShardingPointcutChild);

		Element repositoryShardingAdvisorChild = appDoc.createElementNS(AOP_NAMESPACE_URI, ADVISOR);
		repositoryShardingAdvisorChild.setAttribute(POINTCUT_REF,
				buildBeanName(dataSourceId, REPOSITORY_SHARDING_DATA_SOURCE_POINTCUT));
		repositoryShardingAdvisorChild.setAttribute(ADVICE_REF,
				buildBeanName(dataSourceId, REPOSITORY_SHARDING_DATASOURCE_INTERCEPTOR));
		repositoryShardingAdvisorChild.setAttribute(XSD_MATRIX_ORDER, REPOSITORY_SHARDING_DATA_SOURCE_POINTCUT_ORDER);
		repositoryShardingAopConfigElmt.appendChild(repositoryShardingAdvisorChild);
		return repositoryShardingAopConfigElmt;
	}

	/**
	 * build annotationReadWrite AopConfig Elmt
	 * 
	 * @param appDoc
	 * @param readWriteMetaVO
	 * @return
	 */
	private Element buildAnnotationReadWriteAopConfigElmt(Document appDoc, ReadWriteMetaVO readWriteMetaVO) {
		Element annotationReadWriteAopConfigElmt = appDoc.createElementNS(AOP_NAMESPACE_URI, CONFIG);

		Element annotationReadWritePointcutChild = appDoc.createElementNS(AOP_NAMESPACE_URI, POINTCUT);
		annotationReadWritePointcutChild.setAttribute(XSD_ID,
				buildBeanName(dataSourceId, ANNOTATION_READ_WRITE_DATA_SOURCE_POINTCUT));

		if (readWriteMetaVO != null) {
			annotationReadWritePointcutChild.setAttribute(EXPRESSION, readWriteMetaVO.getPointcutExpression());
		} else {
			annotationReadWritePointcutChild.setAttribute(EXPRESSION, READWRITE_POINTCUT_EXPRESSION);
		}

		annotationReadWriteAopConfigElmt.appendChild(annotationReadWritePointcutChild);

		Element annotationReadWriteAdvisorChild = appDoc.createElementNS(AOP_NAMESPACE_URI, ADVISOR);
		annotationReadWriteAdvisorChild.setAttribute(POINTCUT_REF,
				buildBeanName(dataSourceId, ANNOTATION_READ_WRITE_DATA_SOURCE_POINTCUT));
		annotationReadWriteAdvisorChild.setAttribute(ADVICE_REF,
				buildBeanName(dataSourceId, ANNOTATION_READ_WRITE_DATASOURCE_INTERCEPTOR));
		annotationReadWriteAdvisorChild.setAttribute(XSD_MATRIX_ORDER, ANNOTATION_READWRITE_POINTCUT_ORDER);
		annotationReadWriteAopConfigElmt.appendChild(annotationReadWriteAdvisorChild);
		return annotationReadWriteAopConfigElmt;
	}

	/**
	 * 扫描分表策略
	 */
	private ManagedMap<String, BeanDefinitionHolder> scanTableStrategies(StrategiesClassPathBeanDefinitionScanner scan,
                                                                         String strategiesPackage) {
		ManagedMap<String, BeanDefinitionHolder> strategyMap = new ManagedMap<String, BeanDefinitionHolder>();
		Map<String, String> tableStrategies = new LinkedHashMap<String, String>();

		Set<BeanDefinitionHolder> strategySet = scan.doScan(strategiesPackage);
		for (Iterator<BeanDefinitionHolder> it = strategySet.iterator(); it.hasNext();) {
			BeanDefinitionHolder beanDefinitionHolder = it.next();
			Class<?> clazz = null;
			try {
				// TODO Anders Zhu 下面方式不太好
				clazz = Class.forName(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
			} catch (ClassNotFoundException e) {
				LOGGER.error(e.getMessage());
				continue;
			}
			if (clazz != null) {
				Annotation annotation = clazz.getAnnotation(Strategy.class);
				if (annotation != null) {
					strategyMap.put(((Strategy) annotation).value(), beanDefinitionHolder);
					tableStrategies.put(((Strategy) annotation).value(), clazz.getName());
				}
			}
		}
		//MetricRegister.registerTableStrategies(tableStrategies);
		return strategyMap;
	}

	/**
	 * 扫描分库策略
	 */
	private ManagedMap<String, BeanDefinitionHolder> scanRepositoryStrategies(
			StrategiesClassPathBeanDefinitionScanner scan, String strategiesPackage) {
		ManagedMap<String, BeanDefinitionHolder> strategyMap = new ManagedMap<String, BeanDefinitionHolder>();
		Map<String, String> repositoryStrategies = new LinkedHashMap<String, String>();

		Set<BeanDefinitionHolder> strategySet = scan.doScan(strategiesPackage);
		for (Iterator<BeanDefinitionHolder> it = strategySet.iterator(); it.hasNext();) {
			BeanDefinitionHolder beanDefinitionHolder = it.next();
			Class<?> clazz = null;
			try {
				// TODO Anders Zhu 下面方式不太好
				BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
				// beanDefinition.getPropertyValues().add(DEFAULT_DATASOURCE,
				// defaultDataSource);
				clazz = Class.forName(beanDefinition.getBeanClassName());
			} catch (ClassNotFoundException e) {
				LOGGER.error(e.getMessage());
				continue;
			}
			if (clazz != null) {
				Annotation annotation = clazz.getAnnotation(Strategy.class);
				if (annotation != null) {
					strategyMap.put(((Strategy) annotation).value(), beanDefinitionHolder);
					repositoryStrategies.put(((Strategy) annotation).value(), clazz.getName());
				}
			}
		}
		//MetricRegister.registerRepositoryStrategies(repositoryStrategies);
		return strategyMap;
	}

	/**
	 * 解析数据源
	 */
	private DataSourceMetaVO parseDataSource(Element matrixElement, MatrixDatasourceModel matrixDatasourceModel) {
		DataSourceMetaVO dataSourceMetaVO = new DataSourceMetaVO();
		dataSourceMetaVO.setId(matrixElement.getAttribute(XSD_MATRIX_NAME));
		dataSourceMetaVO.setDbType(matrixDatasourceModel.getType());
		if (StringUtils.isNotBlank(matrixElement.getAttribute(XSD_TRANSACTION_MANAGER))) {
			dataSourceMetaVO.setTransactionManager(matrixElement.getAttribute(XSD_TRANSACTION_MANAGER));
		}
		if (StringUtils.isNotBlank(matrixElement.getAttribute(XSD_MYBATIS_SQL_SESSION_FACTORY))) {
			dataSourceMetaVO.setMyBatisSqlSessionFactory(matrixElement.getAttribute(XSD_MYBATIS_SQL_SESSION_FACTORY));
		}

		NodeList nodeList = matrixElement.getChildNodes();
		if (nodeList != null && nodeList.getLength() > 0) {
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				if (node != null && node instanceof Element) {
					Element ele = (Element) node;
					if (ele.getTagName().endsWith(XSD_PROPERTY)) {
						dataSourceMetaVO.addProperty(ele.getAttribute(XSD_NAME), ele.getAttribute(XSD_VALUE));
					} else if (ele.getTagName().endsWith(XSD_MATRIX_POOL_CONFIGS)) {
						MatrixPoolConfigsMetaVO matrixPoolConfigsMetaVO = parseMatrixPoolConfigsMetaVo(ele);
						dataSourceMetaVO.setDsType(matrixPoolConfigsMetaVO.getPoolType());

						Map<String, MatrixPoolConfigMetaVO> atomPoolConfigMap = getAtomPoolConfigMap(
								matrixPoolConfigsMetaVO);
						List<ReadWriteDataSourceMetaVO> readWriteDataSource = parseReadWriteDataSource(
								matrixDatasourceModel, atomPoolConfigMap);
						dataSourceMetaVO.addReadWriteDataSourceMetaVOList(readWriteDataSource);
					} else if (ele.getTagName().endsWith(XSD_MATRIX_TABLE_SHARDING)) {
						dataSourceMetaVO.setTableShardingMetaVO(parseTableSharding(ele));
					} else if (ele.getTagName().endsWith(XSD_MATRIX_REPOSITORY_SHARDING)) {
						dataSourceMetaVO.setRepositoryShardingMetaVO(parseRepositorySharding(ele));
					} else if (ele.getTagName().endsWith(XSD_READ_WRITE)) {
						dataSourceMetaVO.setReadWriteMetaVO(parseReadWriteVo(ele));
					}
				}
			}

			// 未配置pool-configs，加入默认读写datasource
			if (CollectionUtils.isEmpty(dataSourceMetaVO.getReadWriteDataSourceMetaVOs())) {
				dataSourceMetaVO.addReadWriteDataSourceMetaVOList(
						parseReadWriteDataSource(matrixDatasourceModel, new HashMap<String, MatrixPoolConfigMetaVO>()));
			}

		} else {
			// TODO 解决没有子节点标签报错问题
			dataSourceMetaVO.addReadWriteDataSourceMetaVOList(
					parseReadWriteDataSource(matrixDatasourceModel, new HashMap<String, MatrixPoolConfigMetaVO>()));
		}

		return dataSourceMetaVO;
	}

	/**
	 * 解析读写数据源
	 */
	private List<ReadWriteDataSourceMetaVO> parseReadWriteDataSource(MatrixDatasourceModel matrixDatasourceModel,
			Map<String, MatrixPoolConfigMetaVO> atomPoolConfigMap) {
		List<ReadWriteDataSourceMetaVO> list = new ArrayList<ReadWriteDataSourceMetaVO>();

		List<GroupModel> groups = matrixDatasourceModel.getGroups();
		if (groups != null && groups.size() > 0) {
			for (GroupModel groupModel : groups) {
				ReadWriteDataSourceMetaVO readWriteDataSourceMetaVO = new ReadWriteDataSourceMetaVO();
				readWriteDataSourceMetaVO.setName(groupModel.getGroupName());
				readWriteDataSourceMetaVO.setLoadBalance(groupModel.getLoadBalance());

				List<AtomModel> atoms = groupModel.getAtoms();
				for (AtomModel atomModel : atoms) {
					AtomDataSourceMetaVO atomDataSourceMetaVO = new AtomDataSourceMetaVO();
					atomDataSourceMetaVO.setLogicName(atomModel.getAtomName());
					atomDataSourceMetaVO.setAtomModel(atomModel);

					// 增加hermes的异常埋点,在mysql的driver上面增加异常拦截器
					String param = atomModel.getParam();
					atomModel.setParam(wrapParam(param));

					MatrixPoolConfigMetaVO matrixPoolConfigMetaVO = getMatchPoolConfig(atomPoolConfigMap,
							atomModel.getAtomName());
					if (matrixPoolConfigMetaVO != null) {
						// add pool config properties to AtomDataSource
						// properties
						atomDataSourceMetaVO.addProperties(matrixPoolConfigMetaVO.getProperties());
					}

					if (atomModel.getIsMaster()) {
						readWriteDataSourceMetaVO.setWriteDataSourceMetaVO(atomDataSourceMetaVO);
					} else {
						readWriteDataSourceMetaVO.addReadDataSourceMetaVO(atomDataSourceMetaVO);
					}
				}

				if (readWriteDataSourceMetaVO.getReadDataSourceMetaVOs().size() > 1)
					throw new RuntimeException("Read atom should be only one. Check " + matrixDatasourceModel.getMatrixName() + "." + groupModel.getGroupName() + 
						". If you need more read atom, please contact DBA to use LVS.");
				list.add(readWriteDataSourceMetaVO);
			}
		}

		return list;
	}

	// exceptionInterceptors=com.vip.venus.data.driver.DataSeedExceptionInterceptor
	// 在参数上面加上异常拦截器。
	private String wrapParam(String param) {
		String tempParam = "";
		if (StringUtils.isNotBlank(param)) {
			if (!param.endsWith("&")) {
				tempParam = param + "&";
			} else {
				tempParam = param;
			}

		}
		return tempParam + "exceptionInterceptors=" + DataSeedExceptionInterceptor.class.getName();
	}

	private Map<String, MatrixPoolConfigMetaVO> getAtomPoolConfigMap(MatrixPoolConfigsMetaVO matrixPoolConfigsMetaVO) {
		Map<String, MatrixPoolConfigMetaVO> atomPoolConfigMap = new LinkedHashMap<String, MatrixPoolConfigMetaVO>();
		List<MatrixPoolConfigMetaVO> poolConfigMetaVos = matrixPoolConfigsMetaVO.getPoolConfigMetaVos();
		for (MatrixPoolConfigMetaVO matrixPoolConfigMetaVO : poolConfigMetaVos) {
			String[] atomNameArray = matrixPoolConfigMetaVO.getAtomNames().split(",");
			for (String poolConfigAtomName : atomNameArray) {
				atomPoolConfigMap.put(poolConfigAtomName.trim(), matrixPoolConfigMetaVO);
			}
		}
		return atomPoolConfigMap;
	}

	private MatrixPoolConfigMetaVO getMatchPoolConfig(Map<String, MatrixPoolConfigMetaVO> atomPoolConfigMap,
			String atomName) {
		for (Map.Entry<String, MatrixPoolConfigMetaVO> entry : atomPoolConfigMap.entrySet()) {
			if (Utils.isMatch(entry.getKey(), atomName)) {
				return entry.getValue();
			}
		}
		return null;
	}

	/**
	 * 解析pool configs
	 */
	private MatrixPoolConfigsMetaVO parseMatrixPoolConfigsMetaVo(Element element) {
		MatrixPoolConfigsMetaVO matrixPoolConfigMetaVO = new MatrixPoolConfigsMetaVO();
		matrixPoolConfigMetaVO.setPoolType(element.getAttribute(XSD_MATRIX_POOL_TYPE));

		NodeList nodeList = element.getChildNodes();
		if (nodeList != null && nodeList.getLength() > 0) {
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				if (node != null && node instanceof Element) {
					Element ele = (Element) node;
					if (ele.getTagName().endsWith(XSD_MATRIX_POOL_CONFIG)) {
						matrixPoolConfigMetaVO.addMatrixPoolConfigMetaVO(parsePoolConfig(ele));
					}
				}
			}
		}

		return matrixPoolConfigMetaVO;
	}

	/**
	 * 解析PoolConfig
	 */
	private MatrixPoolConfigMetaVO parsePoolConfig(Element element) {
		MatrixPoolConfigMetaVO matrixPoolConfigMetaVO = new MatrixPoolConfigMetaVO();
		matrixPoolConfigMetaVO.setAtomNames(element.getAttribute(XSD_MATRIX_ATOM_NAMES));

		NodeList nodeList = element.getChildNodes();
		if (nodeList != null && nodeList.getLength() > 0) {
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				if (node != null && node instanceof Element) {
					Element ele = (Element) node;
					matrixPoolConfigMetaVO.addProperty(ele.getAttribute(XSD_NAME), ele.getAttribute(XSD_VALUE));
				}
			}
		}

		return matrixPoolConfigMetaVO;
	}

	/**
	 * 解析分表配置
	 */
	private TableShardingMetaVO parseTableSharding(Element element) {
		TableShardingMetaVO tableShardingMetaVO = new TableShardingMetaVO();
		if (StringUtils.isNotBlank(element.getAttribute(XSD_MATRIX_STRATEGIES_PACKAGE))) {
			tableShardingMetaVO.setStrategiesPackage(element.getAttribute(XSD_MATRIX_STRATEGIES_PACKAGE));
		}

		if (StringUtils.isNotBlank(element.getAttribute(XSD_MATRIX_POINTCUT_EXPRESSION))) {
			tableShardingMetaVO.setPointcutExpression(element.getAttribute(XSD_MATRIX_POINTCUT_EXPRESSION));
		}

		return tableShardingMetaVO;
	}

	/**
	 * 解析分库配置
	 */
	private RepositoryShardingMetaVO parseRepositorySharding(Element element) {
		RepositoryShardingMetaVO repositoryShardingMetaVO = new RepositoryShardingMetaVO();
		if (StringUtils.isNotBlank(element.getAttribute(XSD_MATRIX_STRATEGIES_PACKAGE))) {
			repositoryShardingMetaVO.setStrategiesPackage(element.getAttribute(XSD_MATRIX_STRATEGIES_PACKAGE));
		}

		if (StringUtils.isNotBlank(element.getAttribute(XSD_MATRIX_POINTCUT_EXPRESSION))) {
			repositoryShardingMetaVO.setPointcutExpression(element.getAttribute(XSD_MATRIX_POINTCUT_EXPRESSION));
		}

		return repositoryShardingMetaVO;
	}

	private ReadWriteMetaVO parseReadWriteVo(Element element) {
		ReadWriteMetaVO readWriteMetaVO = new ReadWriteMetaVO();
		if (StringUtils.isNotBlank(element.getAttribute(XSD_MATRIX_POINTCUT_EXPRESSION))) {
			readWriteMetaVO.setPointcutExpression(element.getAttribute(XSD_MATRIX_POINTCUT_EXPRESSION));
		}

		return readWriteMetaVO;
	}

}
